package com.smartcommunity.elasticsearch.face;

import com.smartcommunity.api.device.DeviceApi;
import com.smartcommunity.api.engine.EngineApi;
import com.smartcommunity.elasticsearch.face.req.FaceEsReq;
import com.smartcommunity.es.common.EsIndex;
import com.smartcommunity.es.common.EsRestUtils;
import com.smartcommunity.bean.entity.Camera;
import com.smartcommunity.bean.model.Pager;
import com.smartcommunity.bean.model.Result;
import com.smartcommunity.entity.vid.Face;
import com.smartcommunity.entity.vid.FaceSearchModel;
import com.smartcommunity.utils.BeanMapUtil;
import com.smartcommunity.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName FaceEsApi
 * @Author lilun
 * @Description TODO
 * @Date 2019/12/27 14:01
 * @Version 1.0
 */

@Component
@Slf4j
public class FaceEsApi {

    @Autowired
    private EsRestUtils eSRestUtils;
    @Autowired
    private DeviceApi deviceApi;
    @Autowired
    private EngineApi engineApi;

    public List<Map<String, Object>> searchRecentlyCaptureFace(FaceEsReq faceEsReq) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(faceEsReq.getVillageCode())) {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("villageCode", faceEsReq.getVillageCode()));
        }
        Long startTime = DateUtils.getCurrentTimeBefore(-30);
        Long endTime = DateUtils.getNowTime();
        boolQueryBuilder.must(QueryBuilders.rangeQuery("captureTime").gte(startTime));
        boolQueryBuilder.must(QueryBuilders.rangeQuery("captureTime").lt(endTime));
        boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("cameraId", faceEsReq.getCameraId()));
        SortBuilder sortBuilder = eSRestUtils.builderSort(faceEsReq.getSortField(), faceEsReq.getSortMode());
        try {
            String[] indexs = eSRestUtils.getIndexArray(startTime, endTime, EsIndex.FACE);
            if (null == indexs) {
                return new ArrayList<>();
            }
            return eSRestUtils.page(indexs, boolQueryBuilder, faceEsReq.getFields(), faceEsReq.getPageNum(), faceEsReq.getPageSize(), sortBuilder);
        } catch (Exception e) {
            log.error("es查询错误", e);
        }
        return new ArrayList<>();
    }

    public Pager<Face> searchFaceList(FaceSearchModel model) {
        BoolQueryBuilder boolQueryBuilder = buildQueryBuilder(model);

        SortBuilder sortBuilder = eSRestUtils.builderSort(model.getSortField(), model.getSortModel());
        try {
            String[] indexs;
            //如果条件中有开始时间
            if (model.getStartTime() != null && model.getEndTime() != null) {
                indexs = eSRestUtils.getIndexArray(model.getStartTime(), model.getEndTime(), EsIndex.FACE);
            } else {
                indexs = new String[]{EsIndex.FACE_ALIAS};
            }
            if (null == indexs) {
                return new Pager<>();
            }
            List<Map<String, Object>> list = eSRestUtils.page(indexs, boolQueryBuilder, null, model.getPage(), model.getPageSize(), sortBuilder);
            if (list != null && list.size() > 0) {
                List<Face> faceList = new ArrayList<>();
                Set<String> cameraIds = new HashSet<>();
                list.forEach(map -> {
                    Face face = new Face();
                    map.forEach((key, val) -> eSRestUtils.setFieldValueByFieldName(key, face, val));
                    cameraIds.add(face.getCameraId());
                    faceList.add(face);
                });
                try {
                    //查询相机信息
                    Map<String, Object> cameraSearchParam = new HashMap<>();
                    cameraSearchParam.put("deviceIds", String.join(",", cameraIds));
                    Result<List<Camera>> cameraList = deviceApi.getCameraList(cameraSearchParam);
                    if (cameraList != null && CollectionUtils.isNotEmpty(cameraList.getData())) {
                        //改map集合
                        Map<String, Camera> cameraMap = BeanMapUtil.mapBean(cameraList.getData(), "cameraId");
                        //将camera信息加入到Face中
                        for (Face face : faceList) {
                            if (cameraMap.containsKey(face.getCameraId())) {
                                face.setCameraInfo(cameraMap.get(face.getCameraId()));
                            }
                        }
                    }
                } catch (Exception e1) {
                    log.error("人脸通行记录设备信息查询错误", e1);
                }
                int total = eSRestUtils.getTotalRecords(indexs, boolQueryBuilder).intValue();
                return new Pager<>(model.getPage(), model.getPageSize(), total, faceList);
            }
        } catch (Exception e) {
            log.error("es查询错误", e);
        }
        return new Pager<>();
    }

    /**
     * 通过特征值查询人脸通行记录列表
     */
    public List<Face> searchFeatureList(FaceSearchModel model) {
        if (StringUtils.isNotBlank(model.getFeature()) && model.getTop() > 0) {
            //特征查询
            long l1 = System.currentTimeMillis();
            List<Map<String, Object>> featureRecords = searchFaceFeatureRecords(model);
            long l2 = System.currentTimeMillis();
            log.info("1.特征查询耗时:{} ms,命中 {} 条", l2 - l1, featureRecords.size());
            if (!CollectionUtils.isEmpty(featureRecords)) {
                List<String> accessLogId = new LinkedList<>();
                Map<String, Float> fidScore = new HashMap<>((int) (featureRecords.size() / 0.75f) + 1);
                Set<String> indexSet = new HashSet<>();
                for (Map<String, Object> map : featureRecords) {
                    Object repoObj = map.get("repo");
                    indexSet.add(repoObj.toString());
                    Object fidObj = map.get("fid");
                    Object scoreObj = map.get("score");
                    if (null != fidObj && null != scoreObj) {
                        try {
                            Float score = Float.parseFloat(scoreObj.toString());
                            fidScore.put(fidObj.toString(), score);
                        } catch (NumberFormatException e) {
                            log.error("score 转float失败", e);
                        }
                        accessLogId.add(fidObj.toString());
                    }
                }

                BoolQueryBuilder boolQueryBuilder = buildQueryBuilder(model);
                if (CollectionUtils.isNotEmpty(accessLogId)) {
                    boolQueryBuilder.must(QueryBuilders.termsQuery("accessLogId.keyword", accessLogId));
                }

                try {
                    l1 = System.currentTimeMillis();
                    List<Map<String, Object>> list = eSRestUtils.page(indexSet.toArray(new String[0]), boolQueryBuilder, null, 0, model.getTop(), null);
                    l2 = System.currentTimeMillis();
                    log.info("2.通过特征查询es耗时:{} ms,命中 {} 条", l2 - l1, list==null?0:list.size());
                    if (!CollectionUtils.isEmpty(list)) {
                        List<Face> faceList = new LinkedList<>();
                        for (Map<String, Object> map : list) {
                            Face face = new Face();
                            map.forEach((key, val) -> eSRestUtils.setFieldValueByFieldName(key, face, val));
                            face.setScore(fidScore.get(face.getAccessLogId()));
                            faceList.add(face);
                        }
                        if (StringUtils.isNotBlank(model.getSortField())) {
                            if ("captureTime".equals(model.getSortField())) {
                                if ("desc".equals(model.getSortModel())) {
                                    return faceList.stream().sorted(((o1, o2) -> Long.compare(o2.getCaptureTime(), o1.getCaptureTime()))).collect(Collectors.toList());
                                } else if ("asc".equals(model.getSortModel())) {

                                }
                                //默认升序
                                return faceList.stream().sorted(Comparator.comparingLong(Face::getCaptureTime)).collect(Collectors.toList());
                            } else if ("score".equals(model.getSortField())) {
                                if ("desc".equals(model.getSortModel())) {
                                    //排序
                                    return faceList.stream().sorted(((o1, o2) -> Float.compare(o2.getScore(), o1.getScore()))).collect(Collectors.toList());
                                } else if ("asc".equals(model.getSortModel())) {

                                }
                                return faceList.stream().sorted(Comparator.comparing(Face::getScore)).collect(Collectors.toList());
                            }

                        }
                        return faceList;
                    }
                } catch (IOException e) {
                    log.error("es查询错误", e);
                }
            }
        }
        return new LinkedList<>();
    }

    /**
     * 通过查询条件 构建es查询builder
     * @param model 前端查询条件
     */
    private BoolQueryBuilder buildQueryBuilder(FaceSearchModel model) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(model.getVillageCode())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("villageCode", model.getVillageCode().split(",")));
        }
        if (StringUtils.isNotBlank((model.getAccessLogId()))) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("accessLogId", model.getAccessLogId()));
        }
        if (StringUtils.isNotBlank(model.getCameraId())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("cameraId.keyword", model.getCameraId().split(",")));
        }
        if (StringUtils.isNotBlank(model.getVid())) {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("vid", model.getVid()));
        }
        if (model.getStartTime() != null) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("captureTime").gte(model.getStartTime()));
        }
        if (model.getEndTime() != null) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("captureTime").lt(model.getEndTime()));
        }
        if (StringUtils.isNotBlank(model.getText())) {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("cameraName",model.getText()));
        }
        return boolQueryBuilder;
    }

    /**
     * 通过特征值查询人脸通行记录特征列表
     */
    private List<Map<String, Object>> searchFaceFeatureRecords(FaceSearchModel model) {
        if (StringUtils.isNotBlank(model.getFeature()) && model.getTop() > 0) {
            //特征查询
            Map<String, Object> param = new HashMap<>();
            param.put("top", model.getTop());
            param.put("threshold", model.getThreshold());
            Long startTime;
            Long endTime;
            //如果条件中有开始时间
            if (model.getStartTime() != null && model.getEndTime() != null) {
                startTime = model.getStartTime();
                endTime = model.getEndTime();
            } else {
                //没有时间就默认查询1个月的特征
                startTime = DateUtils.getCurrentTimeBefore(-30);
                endTime = DateUtils.getDayEnd(new Date());
            }
            String[] indexs = eSRestUtils.getIndexArray(startTime, endTime, EsIndex.FACE);
            if (null == indexs) {
                return new LinkedList<>();
            }
            //TODO 特征查询不超过1年
            if (indexs.length > 12) {
                indexs = Arrays.copyOfRange(indexs, 0, 12);
            }
            param.put("start_time", getSecondTimestamp(startTime));
            param.put("end_time", getSecondTimestamp(endTime));
            param.put("feature", model.getFeature());
            //设置特征库
            param.put("repos", indexs);
            if (StringUtils.isNotEmpty(model.getCameraId())) {
                param.put("locations", model.getCameraId().split(","));
            }
            Result<List<Map<String, Object>>> result = engineApi.queryFaceRecords(param);
            if (null != result && result.isOk() && !CollectionUtils.isEmpty(result.getData())) {
                return result.getData();
            }
        }
        return new LinkedList<>();
    }

    public static int getSecondTimestamp(Long date){
        if (null == date) {
            return 0;
        }
        String timestamp = String.valueOf(date);
        int length = timestamp.length();
        if (length > 10) {
            return Integer.valueOf(timestamp.substring(0,length-3));
        } else {
            return 0;
        }
    }
}
